package com.neuralnoise.foaf;

import java.util.ArrayList;
import java.util.List;

import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.sparql.vocabulary.FOAF;
import com.hp.hpl.jena.vocabulary.RDF;
import com.neuralnoise.foaf.core.Document;
import com.neuralnoise.foaf.core.FOAFVocabulary;
import com.neuralnoise.foaf.core.Image;
import com.neuralnoise.foaf.core.MyersBriggsType;
import com.neuralnoise.foaf.core.OnlineAccount;
import com.neuralnoise.foaf.core.Person;
import com.neuralnoise.foaf.core.SpatialThing;
import com.neuralnoise.foaf.core.Thing;

public class ExtendedFOAFConverter {
	
	public static final String NSGEO = "http://www.w3.org/2003/01/geo/wgs84_pos#";
	
	public static Resource fromPerson(Person person, Model associatedModel) {
		return personToResource(person, associatedModel);
	}

	public static Resource fromPerson(Person person) {
		return personToResource(person, ModelFactory.createDefaultModel());
	}

	public static Person toPerson(Resource resource) {
		Person person = new Person(resource.getURI());
		/* Resources */
		// homepage
		for (String uri : getValues(resource, FOAFVocabulary.homepage))
			person.addHomepage(new Document(uri));
		// schoolHomepage
		for (String uri : getValues(resource, FOAFVocabulary.schoolHomepage))
			person.addSchoolHomePage(new Document(uri));
		// workplaceHomepage
		for (String uri : getValues(resource, FOAFVocabulary.workplaceHomepage))
			person.addWorkplaceHomepage(new Document(uri));
		// mbox
		for (String uri : getValues(resource, FOAFVocabulary.mbox))
			person.addMbox(new Thing(uri));
		// workInfoHomepage
		for (String uri : getValues(resource, FOAFVocabulary.workInfoHomepage))
			person.addWorkInfoHomepage(new Document(uri));
		// depiction
		for (String uri : getValues(resource, FOAFVocabulary.depiction))
			person.addDepiction(new Image(uri));
		// account
		for (String uri : getValues(resource, FOAFVocabulary.account))
			person.addAccount(new OnlineAccount(uri));
		// currentProject
		for (String uri : getValues(resource, FOAFVocabulary.currentProject))
			person.addCurrentProject(new Thing(uri));
		// img
		for (String uri : getValues(resource, FOAFVocabulary.img))
			person.addImg(new Image(uri));
		// interest
		for (String uri : getValues(resource, FOAFVocabulary.interest))
			person.addInterest(new Document(uri));
		// knows
		for (String uri : getValues(resource, FOAFVocabulary.knows))
			person.addKnows(new Person(uri));
		// logo
		for (String uri : getValues(resource, FOAFVocabulary.logo))
			person.addLogo(new Thing(uri));
		// openid
		for (String uri : getValues(resource, FOAFVocabulary.openid))
			person.addOpenid(new Document(uri));
		// page
		for (String uri : getValues(resource, FOAFVocabulary.page))
			person.addPage(new Document(uri));	
		// pastProject
		for (String uri : getValues(resource, FOAFVocabulary.pastProject))
			person.addPastProject(new Thing(uri));
		// publications
		for (String uri : getValues(resource, FOAFVocabulary.publications))
			person.addPublication(new Document(uri));
		// tipjar
		for (String uri : getValues(resource, FOAFVocabulary.tipjar))
			person.addTipJar(new Document(uri));
		// topic_interest
		for (String uri : getValues(resource, FOAFVocabulary.topic_interest))
			person.addTopicInterest(new Thing(uri));
		// weblog
		for (String uri : getValues(resource, FOAFVocabulary.weblog))
			person.addWeblog(new Document(uri));
		
		/* Literals */
		// name
		for (String value : getValues(resource, FOAFVocabulary.name))
			person.addName(value);
		// familyname
		for (String value : getValues(resource, FOAFVocabulary.familyName))
			person.addFamilyName(value);
		// givenName
		for (String value : getValues(resource, FOAFVocabulary.givenName))
			person.addGivenName(value);
		// givenName
		for (String value : getValues(resource, FOAFVocabulary.firstName))
			person.addFirstName(value);
		// lastName
		for (String value : getValues(resource, FOAFVocabulary.lastName))
			person.addLastName(value);
		// mbox_sha1sum
		for (String value : getValues(resource, FOAFVocabulary.mbox_sha1sum))
			person.addMboxSha1Sum(value);
		// nick
		for (String value : getValues(resource, FOAFVocabulary.nick))
			person.addNickname(value);	
		// title
		for (String value : getValues(resource, FOAFVocabulary.title))
			person.addTitle(value);
		// aimChatID
		for (String value : getValues(resource, FOAFVocabulary.aimChatID))
			person.addAimChatID(value);
		// icqChatID
		for (String value : getValues(resource, FOAFVocabulary.icqChatID))
			person.addIcqChatID(value);
		// yahooChatID
		for (String value : getValues(resource, FOAFVocabulary.yahooChatID))
			person.addYahooChatID(value);
		// skypeID
		for (String value : getValues(resource, FOAFVocabulary.skypeID))
			person.addSkypeID(value);
		// status
		for (String value : getValues(resource, FOAFVocabulary.status))
			person.setStatus(value);  // should get only one
		// status
		for (String value : getValues(resource, FOAFVocabulary.title))
			person.addTitle(value);
		// status
		for (String value : getValues(resource, FOAFVocabulary.title))
			person.addTitle(value);
		// age
		for (String value : getValues(resource, FOAFVocabulary.age))
			person.setAge(value); // should get only one
		// birthday
		for (String value : getValues(resource, FOAFVocabulary.birthday))
			person.setBirthday(value); // should get only one
		// phone
		for (String value : getValues(resource, FOAFVocabulary.phone))
			person.addPhone(value);
		// plan
		for (String value : getValues(resource, FOAFVocabulary.plan))
			person.addPlan(value); 
		
		return person;
	}

	// Helpers -----------------------------------------------------------------------------------

	private static Resource personToResource(Person person, Model inputModel) {
		// Associate model
		Model model = inputModel;
		// Create primary person
		Resource primaryPerson = model.createResource(person.getUri());
		primaryPerson.addProperty(RDF.type, FOAFVocabulary.Person);

		// Resources	
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.homepage, person.getAllHomepages());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.schoolHomepage, person.getAllSchoolHomePages());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.workplaceHomepage, person.getAllWorkplaceHomepages());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.mbox, person.getAllMboxes());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.workInfoHomepage, person.getAllWorkInfoHomepages());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.depiction, person.getAllDepictions());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.account, person.getAllAccounts());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.currentProject, person.getAllCurrentProjects());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.img, person.getAllImgs());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.interest, person.getAllInterests());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.knows, person.getAllKnows());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.logo, person.getAlllogos());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.openid, person.getAllOpenids());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.page, person.getAllPages());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.pastProject, person.getAllPastProjects());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.publications, person.getAllPublications());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.tipjar, person.getAllTipJars());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.topic_interest, person.getAllTopicInterests());
		addResourcesToModel(model, primaryPerson, FOAFVocabulary.weblog, person.getAllWeblogs());

		//addResourcesToModel(model, primaryPerson, FOAFVocabulary.based_near, person.getAllLocations());	
		if (person.getBaseNear() != null) {
			SpatialThing basedNear = person.getBaseNear();
			Property geo = model.createProperty(NSGEO + "Point");
			if (basedNear.getLatitude() != null) {
				Property lati = model.createProperty(NSGEO + "lat");
				geo.addProperty(lati, basedNear.getLatitude().toString());
			}
			if (basedNear.getLongitude() != null) {
				Property longi = model.createProperty(NSGEO + "long");
				geo.addProperty(longi, basedNear.getLongitude().toString());
			}
			if (basedNear.getAltitude() != null) {
				Property longi = model.createProperty(NSGEO + "alt");
				geo.addProperty(longi, basedNear.getAltitude().toString());
			}
			model.add(primaryPerson, FOAF.based_near, geo);
		}
		
		
		// Literals
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.name, person.getAllNames());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.familyName, person.getAllFamilyNames());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.givenName, person.getAllGivenNames());
		
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.firstName, person.getAllFirstNames());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.lastName, person.getAllLastNames());
		
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.mbox_sha1sum, person.getAllMboxesSha1Sum());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.nick, person.getAllNicknames());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.title, person.getAllTitles());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.aimChatID, person.getAllAimChatIDs());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.icqChatID, person.getAllIcqChatIDs());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.jabberID, person.getAlljabberIDs());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.msnChatID, person.getAllMsnChatIDs());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.yahooChatID, person.getAllYahooChatIDs());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.skypeID, person.getAllSkypeIDs());
		addLiteralToModel(model, primaryPerson, FOAFVocabulary.status, person.getStatus());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.title, person.getAllTitles());
		addLiteralsEnumsToModel(model, primaryPerson, FOAFVocabulary.myersBriggs, person.getAllMyersBriggs());
		addLiteralToModel(model, primaryPerson, FOAFVocabulary.age, person.getAge());
		addLiteralToModel(model, primaryPerson, FOAFVocabulary.birthday, person.getBirthday());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.phone, person.getAllPhones());
		addLiteralsToModel(model, primaryPerson, FOAFVocabulary.plan, person.getAllPlans());

		return primaryPerson;
	}

	private static List<String> getValues(Resource resource, Property property) {
		// list the statements in the resource
		StmtIterator iter = resource.listProperties(property);
		List<String> values = new ArrayList<String>();
		while (iter.hasNext()) {
			Statement stmt = iter.nextStatement(); // get next statement
			RDFNode object = stmt.getObject(); // get the object
			values.add(object.toString());
		}
		return values;
	}

	private static Model addResourcesToModel(Model model, Resource subject, Property property,
			List<? extends Thing> things) {
		for (Thing thing : things) {
			Resource resource = model.createResource(thing.getUri());
			model.add(subject, property, resource);
		}
		return model;
	}

	private static Model addLiteralsToModel(Model model, Resource subject, Property property, List<String> strValues) {
		for (String value : strValues) {
			Literal literal = model.createLiteral(value);
			model.add(subject, property, literal);
		}
		return model;
	}

	private static Model addLiteralsEnumsToModel(Model model, Resource subject, Property property,
			List<MyersBriggsType> list) {
		for (MyersBriggsType value : list) {
			Literal literal = model.createLiteral(value.toString());
			model.add(subject, property, literal);
		}
		return model;
	}

	private static Model addLiteralToModel(Model model, Resource subject, Property property, String strValue) {
		if (strValue != null) { //defensive if
			Literal literal = model.createLiteral(strValue);
			model.add(subject, property, literal);
		}
		return model;
	}
	
}
